package hikari.gui.panels.camera;

import com.gooddies.events.ValueChangedEvent;
import com.gooddies.swing.hCheckBox;
import com.gooddies.texteditors.DefaultFloatTextField;
import com.gooddies.wiring.Wiring;
import com.gooddies.wiring.annotations.PostWiring;
import com.gooddies.wiring.annotations.Wire;
import com.gooddies.wiring.annotations.WiringComponent;
import hikari.gui.guicomponents.hLabel;
import hikari.hcomponents.HComponent;
import hikari.hcomponents.RenderEvent;
import hikari.managers.EngineProperties;
import hikari.managers.Manager3D;
import hikari.objects3d.Camera3dObject;
import hikari.preferences.GameProperties;
import hikari.preferences.SceneProperties;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JSlider;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import net.miginfocom.swing.MigLayout;
import org.swig.jirr.IVideoDriver;
import org.swig.jirr.SViewFrustum;
import org.swig.jirr.vector3df;

/**
 * @author Dmitry
 */
@WiringComponent(singleton = true, lazy = true)
public class CameraPanel extends HComponent {

    private Camera3dObject camera;
    private static CameraPreviewWindow window;
    private DefaultFloatTextField nearClipEditBox;
    private DefaultFloatTextField farClipEditBox;
    private JSlider fovEditBox;
    private DefaultFloatTextField aspectRatioEditBox;
    private JButton makeActiveButton;
    private hCheckBox targetCamera;
    @Wire
    private EngineProperties engineProperties;
    @Wire
    private GameProperties gameProperties;
    @Wire
    private SceneProperties sceneProperties;
    @Wire
    private Manager3D manager3D;

    private CameraPanel() {
    }

    @PostWiring
    private void init() {
        setTitle("Camera properties");
        setLayout(new MigLayout());
        add(new hLabel("FOV"));
        //fovEditBox = new DefaultFloatTextField(0, 360);
        fovEditBox = new JSlider(0, 360);
        add(fovEditBox, "w 100!, wrap");
        fovEditBox.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                fovChanged(fovEditBox.getValue());
            }
        });
        /*fovEditBox.setValueChanged(new ValueChangedEvent<Float>() {
         @Override
         public void valueChanged(Float value) {
         fovChanged(value);
         }
         });*/


        add(new hLabel("Near"));
        nearClipEditBox = new DefaultFloatTextField();
        add(nearClipEditBox, "w 100!, wrap");
        nearClipEditBox.setValueChangedEventNumber(new ValueChangedEvent<Float>() {
            @Override
            public void valueChanged(Float value) {
                nearChanged(value);
            }
        });

        add(new hLabel("Far"));
        farClipEditBox = new DefaultFloatTextField();
        add(farClipEditBox, "w 100!, wrap");
        farClipEditBox.setValueChangedEventNumber(new ValueChangedEvent<Float>() {
            @Override
            public void valueChanged(Float value) {
                farChanged(value);
            }
        });

        add(new hLabel("Aspect Ratio"));
        aspectRatioEditBox = new DefaultFloatTextField();
        aspectRatioEditBox.setValueChangedEventNumber(new ValueChangedEvent<Float>() {
            @Override
            public void valueChanged(Float value) {
                aspectRatioChanged(value);
            }
        });
        add(aspectRatioEditBox, "w 100!, wrap");
        targetCamera = new hCheckBox("Target Camera");
        targetCamera.setValueChangedEvent(new ValueChangedEvent<Boolean>() {
            @Override
            public void valueChanged(Boolean value) {
                targetCameraChanged(value);
            }
        });
        add(targetCamera, "w 100!, wrap, span x 5");
        makeActiveButton = new JButton("Make active camera");
        makeActiveButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                makeActive();
            }
        });

        add(makeActiveButton, "spanx 5");
    }

    public void initialUpdateFromModel(Camera3dObject camera) {
        this.camera = camera;
        window = Wiring.getComponent(CameraPreviewWindow.class);
        window.setTitle(camera.getObjectName());
        int width = gameProperties.getDeviceParameters().getDeviceWidth();
        int height = gameProperties.getDeviceParameters().getDeviceHeight();

        float aspectRatio = (float) width / (float) height;
        window.setAspectRatio(aspectRatio);
        camera.getRealCameraNode().setAspectRatio(aspectRatio);
        window.setVisible(true);
        fovEditBox.setValue((int) Math.toDegrees(camera.getRealCameraNode().getFOV()));
        nearClipEditBox.setValue(camera.getRealCameraNode().getNearValue());
        farClipEditBox.setValue(camera.getRealCameraNode().getFarValue());
        aspectRatioEditBox.setValue(camera.getRealCameraNode().getAspectRatio());
        if (sceneProperties.getMainCamera() == camera) {
            makeActiveButton.setEnabled(false);
        } else {
            makeActiveButton.setEnabled(true);
        }

        targetCamera.setSelected(camera.isTargetCamera());
    }

    private void makeActive() {
        sceneProperties.setMainCamera(camera);
        makeActiveButton.setEnabled(false);
    }

    private void fovChanged(float newFov) {
        camera.getRealCameraNode().setFOV((float) Math.toRadians(newFov));
    }

    private void aspectRatioChanged(float newRatio) {
        window.setAspectRatio(newRatio);
        camera.getRealCameraNode().setAspectRatio(newRatio);
    }

    private void targetCameraChanged(boolean target) {
        camera.setTargetCamera(target);
    }

    private void nearChanged(float newNear) {
        camera.getRealCameraNode().setNearValue(newNear);
    }

    private void farChanged(float newFar) {
        camera.getRealCameraNode().setFarValue(newFar);
    }

    @Override
    public void onClose() {
        camera = null;
        if (window != null) {
            window.setVisible(false);
            window = null;
        }

        manager3D.activateStandardCamera();
    }

    @Override
    public RenderEvent registerAfterRenderBeforeGuiEvent() {
        return new RenderEvent() {
            @Override
            public void onRender() {
                SViewFrustum frustum = camera.getRealCameraNode().getViewFrustum();
                IVideoDriver driver = manager3D.getVideoDriver();
                driver.setMaterial(engineProperties.getSMaterialProperty("selectionBoxMaterial"));
                driver.resetTransformMatrix();

                vector3df fp1 = frustum.getFarLeftUp();
                vector3df fp2 = frustum.getFarRightUp();
                vector3df fp3 = frustum.getFarRightDown();
                vector3df fp4 = frustum.getFarLeftDown();

                vector3df np1 = frustum.getNearLeftUp();
                vector3df np2 = frustum.getNearRightUp();
                vector3df np3 = frustum.getNearRightDown();
                vector3df np4 = frustum.getNearLeftDown();

                driver.draw3DLine(fp1, fp2);
                driver.draw3DLine(fp2, fp3);
                driver.draw3DLine(fp3, fp4);
                driver.draw3DLine(fp4, fp1);

                driver.draw3DLine(np1, np2);
                driver.draw3DLine(np2, np3);
                driver.draw3DLine(np3, np4);
                driver.draw3DLine(np4, np1);

                driver.draw3DLine(fp1, np1);
                driver.draw3DLine(fp2, np2);
                driver.draw3DLine(fp3, np3);
                driver.draw3DLine(fp4, np4);
            }
        };
    }

    @Override
    public RenderEvent registerAfterRenderEvent() {
        return new RenderEvent() {
            @Override
            public void onRender() {
                if (window != null) {
                    manager3D.setDrawingWindowHandle(window.getHWND());
                    manager3D.setDisableInstruments(true);
                    manager3D.getSceneManager().setActiveCamera(camera.getRealCameraNode());
                    manager3D.render();
                    manager3D.activateStandardCamera();
                    manager3D.setDisableInstruments(false);
                    manager3D.resetWindowHandle();
                }
            }
        };
    }
}
